Skip to content

feat(docs): add PackageManagerTabs and register it globally - #433

Merged
allxsmith merged 4 commits into
mainfrom
docs/pm-tabs-component
Aug 1, 2026
Merged

allxsmith merged 4 commits into
mainfrom
docs/pm-tabs-component

Conversation

@allxsmith

@allxsmith allxsmith commented Jul 30, 2026 •

Copy link
Copy Markdown
Owner

Second PR of #402. #418 (the flattener groundwork) has merged, so this targets main directly.

The component half. One install command, four package managers, pnpm first and default.

What's here

  • docs/src/components/PackageManagerTabs/index.js — consumes the translate.mjs vocabulary that landed with the flattener in chore(docs): make the LLM flattener indentation-aware before the #402 rollout #418, so the pnpm tab and llms.txt cannot drift.
  • docs/src/theme/MDXComponents.js — global registration.
  • One conversion, guides/intro.md "Install Dependencies", to prove the pipeline rather than landing the component unused.

Two decisions worth reviewing

Global registration over per-file imports. ~20 pages will use this and none of the 135 .md files carries an import line today. Per-file imports would put churn in exactly the diffs a reviewer needs to read closely — the ones nested inside numbered steps. Forgetting a global registration is still a loud failure (MDX throws "Expected component PackageManagerTabs to be defined" during the SSR prerender, naming the file), so this costs no safety. The tradeoff accepted: reading raw source on GitHub shows an element with no visible definition.

Shared groupId="package-manager". This is the reason to use @theme/Tabs rather than hand-rolling a switcher: Docusaurus persists the choice in localStorage, so picking npm on one page selects npm on every other page. The homepage hero switcher will read the same slot in the next PR. Note the tab values must stay exactly the PACKAGE_MANAGERS strings — Docusaurus silently discards a stored value that isn't valid for the group.

Verified in the browser

Dev server, /docs/guides/intro:

  • Four tabs render, pnpm active by default.
  • Each tab shows the right translation:
    pnpm add @allxsmith/bestax-bulma
    npm install @allxsmith/bestax-bulma
    yarn add @allxsmith/bestax-bulma
    bun add @allxsmith/bestax-bulma
    
  • Clicking npm switches the panel and writes docusaurus.tab.package-manager; the selection survives a reload.
  • No console errors and no hydration warnings.
  • The swizzled CodeBlock's copy button still appears inside the tab panel (non-live blocks pass through untouched, as expected).
  • At 375px in dark mode all four labels fit on one line, tab bar doesn't overflow, no horizontal page scroll.

Verified in the artifacts

The flattened build/docs/guides/intro.md twin reproduces the original fence exactly:

### Install Dependencies

```bash
pnpm add @allxsmith/bestax-bulma
```

So diff -r over *.md twins and llms*.txt against a pre-conversion baseline build is empty. The conversion is invisible to agents — that's the invariant every later batch gets checked against, and the reason it's worth establishing on one boring fence first.

verifyArtifact reports clean on llms.txt, llms-full.txt and the converted twin. 70 tests pass, check:conformance 8/8, prettier clean.

Next

PR 2 is the homepage hero switcher; PRs 3-6 convert the docs pages, starting with the two riskiest shapes (the 2-space bulleted case and the fake-numbered-list case) before the 18-fence react-setups.md.

Summary by CodeRabbit

  • New Features

    • Added synchronized package-manager tabs for npm, Yarn, pnpm, and Bun installation commands.
    • Documentation can now use canonical pnpm commands that automatically translate to equivalent package-manager instructions.
    • Added consistent styling and tab selection persistence across documentation pages.
  • Documentation

    • Updated authoring guidance for package-manager tabs and JSX formatting.
    • Clarified how generated documentation handles tab content and code examples.
  • Refactor

    • Simplified documentation builds by removing post-build tab conversion and related validation workflows.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The PR replaces post-build LLM tab flattening with a PackageManagerTabs MDX component. It adds canonical pnpm command translation, validation, synchronized manager tabs, styling, documentation guidance, and related test updates.

Changes

Package manager tabs

Layer / File(s) Summary
Package manager translation contract
docs/src/components/PackageManagerTabs/translate.mjs, docs/scripts/package-manager-translate.test.mjs
Defines tab constants, frozen-install translations, and pnpm round-trip conversion. Tests validate the updated behavior.
MDX component integration
docs/src/components/PackageManagerTabs/*, docs/src/theme/MDXComponents.js, docs/docs/guides/intro.md, docs/CLAUDE.md
Adds PackageManagerTabs, validates canonical pnpm fences, renders synchronized manager tabs, exposes the component globally, and updates authoring guidance.
Build pipeline and flattener cleanup
docs/package.json, docs/scripts/flatten-llms-tabs.mjs, docs/scripts/flatten-llms-*.test.mjs, docs/CLAUDE.md
Removes the post-build flattener, its tests, and its build integration. Documents source-level JSX handling by docusaurus-plugin-llms.
Package manager tab styling
docs/src/components/PackageManagerTabs/styles.module.css
Adds spacing, monospace tab-label styling, adjusted padding, and final-panel margin cleanup.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DocumentationPage
  participant MDXComponents
  participant PackageManagerTabs
  participant Translator
  DocumentationPage->>MDXComponents: Render PackageManagerTabs
  MDXComponents->>PackageManagerTabs: Pass MDX children
  PackageManagerTabs->>Translator: Validate and translate pnpm command
  Translator-->>PackageManagerTabs: Return manager-specific commands
  PackageManagerTabs-->>DocumentationPage: Render synchronized tabs
Loading

Possibly related issues

Possibly related PRs

  • allxsmith/bestax#408 — Introduced the post-build flattener that this PR replaces.
  • allxsmith/bestax#418 — Added related flattener, verification, and translation changes removed here.
  • allxsmith/bestax#451 — Used the earlier .llms-src and tab-flattening workflow replaced by native component handling.

Suggested reviewers: bestaxbot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding and globally registering the PackageManagerTabs component.
Description check ✅ Passed The description provides detailed scope, implementation rationale, verification results, and related PR context, although it omits the template headings and checklist selections.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/pm-tabs-component

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://08eac494.bestax.pages.dev

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a reusable Docusaurus MDX component to render package-manager-specific install commands (pnpm/nvm/yarn/bun), registers it globally for use across docs without per-page imports, and converts one existing docs snippet to validate the end-to-end pipeline.

Changes:

  • Introduces PackageManagerTabs (Tabs/TabItem + CodeBlock) that derives npm/yarn/bun commands from the shared translate.mjs vocabulary.
  • Registers PackageManagerTabs globally via src/theme/MDXComponents.js.
  • Converts docs/docs/guides/intro.md “Install Dependencies” to use <PackageManagerTabs ... />.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
docs/src/theme/MDXComponents.js Registers PackageManagerTabs globally in MDX so docs pages can use it without imports.
docs/src/components/PackageManagerTabs/index.js New component rendering Docusaurus tabs + code blocks for each package manager.
docs/src/components/PackageManagerTabs/styles.module.css Styles for tighter spacing and monospace tab labels.
docs/docs/guides/intro.md Replaces a pnpm-only code fence with <PackageManagerTabs command="..." />.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +25 to +30
export default function PackageManagerTabs({ command }) {
if (process.env.NODE_ENV === 'development') {
for (const warning of lintCommand(command)) {
console.warn(`PackageManagerTabs: ${warning}`);
}
}
Comment on lines +33 to +35
<div className={styles.tabs}>
<Tabs groupId="package-manager" defaultValue={PACKAGE_MANAGERS[0]}>
{PACKAGE_MANAGERS.map(manager => (

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deep review — 0 blocking · 2 advisory

# Severity Area Finding Location
1 🔵 Advisory Robustness command="" (empty) renders four empty code blocks and passes the flatten gate as an empty ```bash fence — no dev warning, silent docs/src/components/PackageManagerTabs/index.js:26
2 🔵 Advisory Robustness The panel-margin reset keys off Docusaurus's internal hashed class [class*='tabItem_']; a theme upgrade could silently no-op it (cosmetic only) docs/src/components/PackageManagerTabs/styles.module.css:18

Overall: The change is sound and well-defended. The load-bearing invariant — "the pnpm tab a reader sees equals what an agent copies out of llms.txt" — holds by construction, because both the component and flatten-llms-tabs.mjs call the same renderCommand(cmd, 'pnpm') from the shared translate.mjs, and I confirmed the one converted fence (add @allxsmith/bestax-bulma) round-trips byte-identically to the original. Two independent CI safety nets back it: a missing global registration throws during the docusaurus build SSR prerender (naming the file), and any unflattened <PackageManagerTabs> JSX fails verifyArtifact — and I verified pnpm run build (turbo → docs build → the flattener) runs in CI at .github/workflows/ci.yml:52, so both nets fire on PRs. The JSX-without-React-import and the safe @theme-original/MDXComponents spread both match existing repo precedent (HomepageFeatures, the swizzled CodeBlock). The riskiest surface is the shared groupId="package-manager" contract the next PR's hero switcher must honor, but that's out of scope here. Human can merge; nothing needs action first.

Residual risk: the addressed failure class is drift between the rendered pnpm tab and the LLM artifacts.

  • Missing / misspelled command prop — refuted: the flattener regex requires a literal command="...", so <PackageManagerTabs /> or a typo'd attr leaves raw JSX that fails the gate and reddens CI.
  • Whitespace / verb-swap drift — refuted: renderCommand is imported, not reimplemented, in the flattener; pnpm is a pure pnpm ${segment} prefix over the same normalized segment, so component and artifact cannot diverge.
  • Empty-string command — not fully refuted (advisory #1): it renders and flattens to an empty fence with no warning — an unlikely authoring slip, no drift, cosmetic only.

🏄 Clean little set wave, dude — one boring fence, but the pnpm-in-equals-pnpm-out current runs true end to end and the CI reef will chew up anything that drifts. Paddle it out, it's good to go.

The component half of #402. Renders one install command for pnpm, npm, yarn
and bun, consuming the translate.mjs vocabulary added alongside the flattener.

Registered in a new src/theme/MDXComponents.js rather than imported per page.
~20 pages will use it and none of the 135 .md files carries an import today, so
per-file imports would put churn in exactly the diffs a reviewer needs to read
closely — the ones nested in numbered steps. A missing global registration is
still a loud failure (MDX throws during SSR prerender, naming the file), so this
costs no safety.

Tabs share groupId="package-manager", which is the reason to use @theme/Tabs
rather than hand-rolling: Docusaurus persists the choice, so picking npm on one
page selects npm everywhere, and the homepage hero switcher can later read the
same slot.

Converts one column-0 fence (guides/intro.md "Install Dependencies") to prove
the pipeline end to end rather than landing the component unused.

Verified in the browser: four tabs render with pnpm active by default, each tab
shows the right translation, the selection persists across a reload, no console
errors or hydration warnings, the swizzled CodeBlock's copy button still
appears, and at 375px in dark mode all four labels fit on one line without
overflowing.

Verified in the artifacts: the flattened guides/intro.md twin reproduces the
original fence exactly, so the LLM output is byte-identical to a pre-conversion
baseline build. The conversion is invisible to agents, which is the invariant
every later batch will be checked against.
Review follow-ups on #433.

An omitted or empty `command` now throws instead of rendering. Deliberately not
gated on NODE_ENV: `docusaurus build` prerenders every page with
NODE_ENV=production, so an unconditional throw is what turns an authoring slip
into a failed build naming the page — a dev-only check would miss it in CI.
The flatten gate already catches a *missing* attribute, since its regex
requires a literal command="…"; it cannot see command="" or command=" ; ",
which flatten to a silent empty bash fence. Because the prerender covers every
page, this can never fire in a browser on a site that built successfully.

The default tab is now a named DEFAULT_PACKAGE_MANAGER rather than
PACKAGE_MANAGERS[0], so tab order and the default can move independently. It
is not merely cosmetic: the flattener collapses every tab group to the pnpm
rendering, so the default is what makes the page agree with the artifact.

TAB_GROUP_ID and TAB_STORAGE_KEY move into translate.mjs. The group id and the
localStorage key it derives were about to be hardcoded in two places — the tab
group here and the homepage hero switcher in #434 — where a rename would
silently degrade to "hero and docs no longer share a choice" with nothing
failing.

Also documents the authoring convention in docs/CLAUDE.md, which described the
component but never said how to write one, and records that .md renders JSX
here because markdown.format defaults to mdx.

Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh
@allxsmith

Copy link
Copy Markdown
Owner Author

Rebased onto main, review fixes applied — and one blocker found

Rebased cleanly onto main (22 commits; zero file overlap, no conflicts).

Review fixes — 5b0d3e6

Copilot 1 + deep review advisory 1 (command undefined / empty). Both close together: an
omitted or empty command now throws. Deliberately not gated on NODE_ENV — docusaurus build prerenders every page with NODE_ENV=production, so an unconditional throw is what
turns an authoring slip into a failed build naming the page; a dev-only check would miss it in
CI entirely. The flatten gate already catches a missing attribute (its regex requires a
literal command="…"), but cannot see command="" or command=" ; ", which flatten to a
silent empty ```bash fence. Because the prerender covers every page, this can never fire in
a browser on a site that built successfully.

Copilot 2 (default tab coupled to array order). Now a named DEFAULT_PACKAGE_MANAGER.
Worth stating why it isn't cosmetic: the flattener collapses every tab group to the pnpm
rendering, so the default tab is what makes the rendered page agree with the LLM artifact.

Deep review advisory 2 ([class*='tabItem_']). Left as-is with a comment naming the theme
internal it couples to. .tabs__item is a stable Infima class but the tab panel only has a
hashed CSS-module name, so a loose match is the honest option; the failure mode is a regained
bottom margin, visible on sight.

Not from review, but it would have bitten #434: groupId="package-manager" here and
'docusaurus.tab.package-manager' in #434 were about to be two hardcoded strings with nothing
tying them together. A rename would have silently degraded to "hero and docs no longer share a
choice", with nothing failing. Both now come from TAB_GROUP_ID / TAB_STORAGE_KEY in
translate.mjs, which both files already import.

Also added the authoring convention to docs/CLAUDE.md — it documented the component but never
said how to write one — plus a note that .md renders JSX here because markdown.format
defaults to mdx, which intro.md is the first page to rely on.

Blocked on #451

While verifying the build I found that this PR's intro.md conversion silently deletes the
install command from the LLM artifacts
. Not this PR's fault: docusaurus-plugin-llms 0.5.0
strips PascalCase JSX tags, and content carried in a prop goes with the tag. Since
<PackageManagerTabs command="…" /> is self-closing, its whole payload is the prop.

The same bug is already live on main for <TabItem label="…"> — those labels are missing
from llms-full.txt today. #451 fixes the ordering (flatten into a mirror before the plugin
reads it) and adds tests for the failure class.

Merge #451 first. Until it lands, this PR would ship a conversion whose entire point —
readers and agents seeing the same command — is defeated for agents.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://7e0b1ed4.bestax.pages.dev

…ener

The plugin that generates llms.txt strips PascalCase JSX tags and keeps their
inner text. Content in a *prop* is not inner text — it goes with the tag — so a
self-closing <PackageManagerTabs command="…" /> left an empty section in every
generated artifact while the rendered site looked fine.

Rather than post-process the artifacts, put the command where the rule already
preserves it. A page now wraps the pnpm fence it would have written anyway:

    <PackageManagerTabs>

    ```bash
    pnpm add @allxsmith/bestax-bulma
    ```

    </PackageManagerTabs>

The plugin removes the wrapper and leaves exactly that fence, so the artifact is
correct by construction — no build step, no config, nothing to keep in sync.
Confirmed by running the plugin's own cleanMarkdownContent over both shapes: the
prop form yields an empty section, the children form yields the fence.

The fence is the single source of truth. The component recovers the authoring
vocabulary from it by stripping the `pnpm ` prefix line-wise, derives npm, yarn
and bun from that, and asserts the round trip renders back to the fence exactly
— so a non-canonical fence fails the prerender instead of producing three tabs
derived from something the page never showed.

That makes scripts/flatten-llms-tabs.mjs dead: its PackageManagerTabs path is
now redundant with the plugin, and its <Tabs> path can no longer work either,
since the labels it linearizes are props the plugin removes before the script
runs. Deleted with its four test suites, including the indentation suite whose
entire reason for existing was re-indenting a fence the script used to
synthesize. The two flattener assertions in the translate suite are replaced by
the identity the design now rests on: the pnpm rendering round-trips back to the
authored command.

Known and accepted: <TabItem label="…"> labels still vanish from the artifacts,
because label is Docusaurus's own prop and cannot move to children. Documented
in docs/CLAUDE.md and filed upstream as rachfop/docusaurus-plugin-llms#64.

Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://c618e5c6.bestax.pages.dev

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (1)

docs/src/components/PackageManagerTabs/index.js:88

  • PackageManagerTabs can accept literal fenced-markdown text (e.g. when the fence inside the JSX block isn’t parsed as a code block due to missing blank lines). In that case textOf(children) may include lines like bash/, and the current “round trip” check still passes because those lines are treated as passthrough segments—resulting in rendered tabs that show fence markers inside the code block instead of failing the build.
export default function PackageManagerTabs({ children }) {
  // The fence as authored, minus the trailing newline MDX leaves on a code block.
  const authored = textOf(children).replace(/\s+$/, '');

  // Throws rather than rendering something wrong, and deliberately not gated on
  // NODE_ENV: `docusaurus build` prerenders every page with NODE_ENV=production,
  // so an unconditional throw is what turns an authoring slip into a failed build
  // that names the page. A dev-only check would miss it in CI entirely — and
  // because the prerender covers every page, this can never fire in a browser on
  // a site that built successfully.
  if (!authored) {
    throw new Error(
      'PackageManagerTabs: expected a pnpm code fence as its children, e.g.\n' +
        '<PackageManagerTabs>\n\n```bash\npnpm add @allxsmith/bestax-bulma\n```\n\n</PackageManagerTabs>'
    );
  }

@allxsmith

Copy link
Copy Markdown
Owner Author

@claude please re Review, lots have changed

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deep review — 0 blocking · 3 advisory

# Severity Area Finding Location
1 🔵 Advisory Correctness install --frozen-lockfile translates to npm install --frozen-lockfile, which npm ignores (it does a normal, lockfile-mutating install); the intended npm equivalent is npm ci. Not reached by this PR's one conversion, but a landmine for the conversion PRs. docs/src/components/PackageManagerTabs/translate.mjs:105
2 🔵 Advisory Robustness Deleting the flattener means generic <Tabs>/<TabItem label="…"> no longer promote labels to headings in the LLM artifacts — the bodies concatenate with nothing marking which option is which (skills/theming.mdx, skills/custom-component.mdx). docs/CLAUDE.md:39
3 🔵 Advisory Coverage The component's own logic (textOf, the pnpm-prefix unrender, and both prerender throws) has no unit test — docs has no jest/RTL harness, so only translate.mjs is exercised by node --test. The prerender throw is the only safety net. docs/src/components/PackageManagerTabs/index.js:64

Overall: The change is sound and the central design claim holds up under scrutiny. I confirmed empirically against the installed docusaurus-plugin-llms@0.5.1 that cleanMarkdownContent masks code fences before it strips PascalCase tags (content.js:204 then :221), so a fence wrapped in <PackageManagerTabs> survives verbatim while the wrapper is removed — the artifact really is correct by construction, and the deleted flattener/gate is genuinely redundant for this shape. The round-trip throw in index.js is a solid replacement safety net for the one component it guards. The riskiest part is not this PR but what it sets up: the translation table (item 1) and the label-loss regression (item 2) become user-visible only as later PRs convert real pages. A human should focus on item 1 before the contributing.md/security.md conversions land.

Residual risk: the failure class here is "the machine-readable artifact silently diverges from the rendered page."

  • Fence dropped from the artifact — refuted: verified the plugin keeps masked code and strips only the wrapper tag; the per-page twin and llms-full.txt both reduce to the pnpm fence.
  • A bad/non-canonical fence renders 3 wrong tabs — refuted: the unconditional round-trip throw at index.js:102 fails the production prerender (which covers every page) before the artifact is written.
  • Wrong translated command reaches a reader — open (item 1): pnpm install --frozen-lockfile exists today at contributing.md:47, and security.md:40 itself states the npm equivalent is npm ci — so when that page is converted, the npm tab will contradict the library's own guidance. No page emits it yet, hence advisory not blocking.

🏄 Clean set, dude — the fence rides straight through the plugin with the wrapper wiped, no wipeout. Just watch that --frozen-lockfile reef when you paddle out to the next conversion; it's calm water today but it'll bite npm surfers later. Good to go.

@claude

claude Bot commented Aug 1, 2026 •

Copy link
Copy Markdown

Claude finished @allxsmith's task in 3m 56s —— View job


Re-reviewing PR #433

  • Gather context (diff since last review, rebase history, prior review threads)
  • Understand request (re-review after "lots have changed")
  • Read changed files
  • Check the Copilot "suppressed comment" about literal fenced-markdown text bypassing the round-trip check
  • Check status of blocker mentioned in PR (dependency on fix(docs): flatten tab JSX before the LLM plugin reads it, not after #451)
  • Post review findings

What changed since the last review

Commit a0935ce is a real architecture change, not a fixup: it drops the LLM flattener entirely (scripts/flatten-llms-tabs.mjs and its ~1,300 lines of tests) and switches PackageManagerTabs from a command prop to children — the component now takes a real pnpm code fence:

<PackageManagerTabs>

```bash
pnpm add @allxsmith/bestax-bulma
```

This is the right fix for the blocker the author called out on 2026-08-01 (the command-prop approach silently lost the install command from llms.txt/llms-full.txt because docusaurus-plugin-llms strips PascalCase JSX tags and drops prop content with the tag). Content in children survives the strip; content in props doesn't. So this PR now resolves the #451 blocker itself rather than depending on it — confirmed no dangling references to the deleted flatten-llms-* scripts remain anywhere (package.json, CI, other scripts).

Also confirmed already fixed from the prior review round:

  • Copilot's "coerces to the string undefined" finding — now throws on empty/missing content (index.js:83).
  • Copilot's "default tab coupled to array order" finding — now a named DEFAULT_PACKAGE_MANAGER (translate.mjs:44).
  • The hardcoded groupId/localStorage-key strings — now TAB_GROUP_ID/TAB_STORAGE_KEY shared constants, so PR feat(docs): add package-manager switches to the homepage hero #434's hero switcher can't drift from this one by a typo.

Finding: the round-trip guard has a real gap (the "suppressed" Copilot comment is correct)

The 20:06 Copilot review flagged this as a suppressed/low-confidence comment on index.js:88, but tracing it by hand through the current code confirms it's a genuine bug, not a false positive.

docs/CLAUDE.md's own new section states the safety guarantee plainly:

Blank lines around the fence are required — without them MDX treats it as literal text, not a code block. The component derives the command back out of the fence and throws during the prerender if the round trip isn't exact, so a non-canonical fence ... fails the build rather than rendering three tabs derived from something the page never showed.

That guarantee doesn't hold for exactly the failure mode the same paragraph warns about. If an author drops the blank line around the fence, MDX doesn't parse it as a code block, so textOf(children) in docs/src/components/PackageManagerTabs/index.js:64-71 returns the literal source text, backticks included:

```bash
pnpm add @allxsmith/bestax-bulma

Trace it through `index.js:93-109`:
- `command` becomes `` "```bash; add @allxsmith/bestax-bulma; ```" `` (the ` ```bash ` and ` ``` ` lines have no space, so `.replace(/^pnpm /, '')` is a no-op on them).
- In `translate.mjs`'s `translateSegment` (line 87), a segment whose first token isn't a known verb is returned **unchanged, for every manager** — before the `pnpm`-specific branch even runs. So the ` ```bash ` and ` ``` ` segments round-trip byte-for-byte back to themselves regardless of manager.
- `renderCommand(command, DEFAULT_PACKAGE_MANAGER)` therefore reconstructs the exact original string, the `roundTrip !== authored` check at `index.js:102` passes, and **no error is thrown**.
- But `renderCommand(command, 'npm')` (and yarn/bun) still passes those two segments through unchanged while translating the middle one — so the npm/yarn/bun tabs render a `CodeBlock` containing the literal text `` ```bash `` / `npm install ...` / ` ``` ` as three lines, fence markers included, instead of a clean command.

Net effect: the one authoring mistake the design is explicitly built to turn into a build failure — a missing blank line — instead silently ships three tabs with visible markdown fence syntax in them. This isn't reachable through the one page converted in this PR (`intro.md` has the blank lines), but the PR description says ~20 pages are converting to this pattern next, and a missing blank line is an easy, realistic slip in one of those.

**Suggested fix:** before deriving `command` (`index.js:90`), throw if any line of `authored` (trimmed) starts with `` ``` `` — a properly-parsed fence's extracted text never contains the fence delimiters, so this reliably distinguishes "MDX parsed a real code block" from "MDX treated this as literal text," with a message pointing at the missing-blank-line requirement.

[Fix this →](https://claude.ai/code?q=docs%2Fsrc%2Fcomponents%2FPackageManagerTabs%2Findex.js%20line%20~101%20on%20branch%20docs%2Fpm-tabs-component%3A%20the%20round-trip%20guard%20doesn't%20throw%20when%20MDX%20fails%20to%20parse%20the%20pnpm%20fence%20as%20a%20real%20code%20block%20(missing%20blank%20lines%2C%20see%20docs%2FCLAUDE.md).%20textOf(children)%20then%20returns%20literal%20text%20including%20the%20triple-backtick%20fence%20lines%3B%20those%20lines%20aren't%20a%20known%20verb%20so%20they%20pass%20through%20renderCommand%20unchanged%20on%20every%20manager%2C%20the%20round%20trip%20still%20matches%2C%20and%20npm%2Fyarn%2Fbun%20tabs%20render%20the%20literal%20backtick%20markers%20as%20text%20instead%20of%20the%20build%20failing.%20Add%20a%20check%20before%20computing%20command%20that%20throws%20if%20any%20line%20of%20authored%20(trimmed)%20starts%20with%20three%20backticks%2C%20pointing%20at%20the%20missing-blank-line%20requirement.&repo=allxsmith/bestax)

### Everything else

- `docs/CLAUDE.md`'s new sections accurately describe the children-vs-props distinction and the authoring convention, and correctly flag `<TabItem label="…">` as a known casualty of the same plugin behavior — good, that's tracked separately (rachfop/docusaurus-plugin-llms#64) rather than silently left inconsistent.
- `docs/scripts/package-manager-translate.test.mjs` was updated consistently with the flattener's removal — it now tests the round-trip identity directly against `translate.mjs` rather than through the deleted `transform()` function. No test covers the unparsed-fence case above; worth adding alongside the fix.
- `styles.module.css`'s `[class*='tabItem_']` coupling to a Docusaurus internal (deep-review advisory #2 from the prior round) is left as-is with a comment explaining the tradeoff — reasonable, since the failure mode is cosmetic and visible on sight.
- Not verified in this pass: I wasn't able to execute `pnpm test`/`pnpm build`/`node` in this sandbox (commands requiring script execution were blocked by the environment's approval gate) — the round-trip gap above was confirmed by hand-tracing `translate.mjs`'s logic against the exact input, not by running it. Worth confirming with a quick local repro before merging if that matters for confidence.

### Summary

The children-based rewrite is a solid design move — it makes the artifact-fidelity guarantee structural (fence content literally survives the plugin's strip) rather than dependent on the now-deleted flattener staying in sync, and it cleans up the two Copilot findings plus the two advisories from the prior review round in the process. The one open gap is real: the round-trip guard's "throw rather than render something wrong" promise has a hole for un-parsed fences, which is exactly the failure mode `docs/CLAUDE.md` calls out to authors as something to watch for. Given ~20 pages are about to adopt this pattern, I'd fix that before merging rather than after the first page trips it.

…ences

Review follow-ups on #433.

**`install --frozen-lockfile` was wrong for npm and yarn.** npm has no such
flag — `npm ci` *is* the frozen install — so `npm install --frozen-lockfile`
did a normal, lockfile-mutating install: the opposite of what the reader asked
for, and silent. guides/security.md already tells readers the npm equivalent is
`npm ci`, so converting that page would have made it contradict itself. Yarn
now gets Berry's `--immutable`, matching the `dlx` line that already targets
Berry over Classic. bun takes the flag as written. No page emits this yet; it
would have surfaced on the first conversion PR.

**A leaked fence delimiter now throws.** If MDX doesn't parse the block —
almost always the missing blank lines around it — the ``` delimiters arrive as
text. The round-trip assertion cannot catch that: ```bash is not a known verb,
so it passes through untouched on every tab and the equality still holds, while
the tabs render fence markers inside the code block. Checked explicitly, with a
message naming the actual mistake. There is a test asserting the round trip
provably does *not* catch this, so the guard can't be removed as redundant.

**The pnpm inverse moves into translate.mjs as `unrenderPnpm`.** It was inlined
in a JSX file, where `node --test` cannot reach it — the round trip is the
design's central invariant and its inverse had no direct coverage. Component
and tests now share one definition.

Not addressed, deliberately: <TabItem label> loss (already documented here and
filed upstream), and full unit coverage of the component itself, which would
need a jest/RTL harness `docs` does not have — the prerender throws run over
every page on every build, which is why they are unconditional.

Claude-Session: https://claude.ai/code/session_01TGA6sFTUGsJ6oXhfpjKEnh
@allxsmith

Copy link
Copy Markdown
Owner Author

Review round 2 — 2 fixed, 2 acknowledged

Deep review 1 — install --frozen-lockfile → wrong for npm and yarn. Fixed.

Good catch, and worse than advisory once you follow it through. npm has no
--frozen-lockfile; npm ci is the frozen install, so npm install --frozen-lockfile
did a normal, lockfile-mutating install — the opposite of what the reader asked for, silently.
And guides/security.md:40 already tells readers the npm equivalent is npm ci, so converting
that page would have made it contradict itself on the same screen.

Yarn was wrong too, which the review didn't flag: --frozen-lockfile is Yarn Classic. Berry
spells it --immutable, and translate.mjs already targets Berry for dlx ("Yarn Classic has
no dlx"), so it was internally inconsistent. bun takes the flag as written.

before after
pnpm pnpm install --frozen-lockfile unchanged
npm npm install --frozen-lockfile npm ci
yarn yarn install --frozen-lockfile yarn install --immutable
bun bun install --frozen-lockfile unchanged

Plain install is unaffected. The table test covers all four.

Copilot (suppressed) — a leaked fence delimiter. Fixed, and it was real.

This one deserved not to be suppressed. If MDX doesn't parse the block — the missing blank
lines, which is the likely authoring slip and something docs/CLAUDE.md now warns about —
the ``` delimiters arrive as text. I reproduced it:

authored  : "```bash\npnpm add foo\n```"
roundTrip : "```bash\npnpm add foo\n```"   ← equal, so the assertion passes
npm tab   : "```bash\nnpm install foo\n```"

The round trip provably cannot catch it: ```bash isn't a known verb, so it passes
through untouched on every tab and the equality still holds — while the tabs render fence
markers inside the code block. Now checked explicitly, with a message naming the real mistake.
There's a test asserting the round trip does not catch this, so the guard can't later be
deleted as redundant.

Deep review 3 — component coverage. Partly addressed.

The pnpm inverse moved into translate.mjs as unrenderPnpm and is now unit-tested; it was
inlined in a JSX file where node --test can't reach it, which was the weakest spot given the
round trip is the design's central invariant. Component and tests now share one definition.

textOf and the throws still have no direct unit test — that needs a jest/RTL harness docs
doesn't have, and adding one is out of scope here. Mitigation worth stating: the throws are
unconditional precisely so the production prerender exercises them across all 144 pages on
every build, so a broken textOf fails the build rather than shipping.

Deep review 2 — <Tabs> label loss. Acknowledged, no change.

Known and deliberate, documented in docs/CLAUDE.md with the two affected pages named, and
filed upstream as rachfop/docusaurus-plugin-llms#64.


21 tests pass, build clean, lint and format clean.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://0ac2f6b9.bestax.pages.dev

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/package.json`:
- Line 9: Update the docs build script while preserving a generic tab-label
transformation for standard Docusaurus tabs before generating published LLM
artifacts. Do not rely solely on PackageManagerTabs; retain or replace the
functionality previously provided by flatten-llms-tabs.mjs so affected Markdown
includes each tab’s option label.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fa89b537-fa07-47b0-9d7e-3dbb88d1db39

📥 Commits

Reviewing files that changed from the base of the PR and between e0af2d0 and 1883de3.

📒 Files selected for processing (13)
  • docs/CLAUDE.md
  • docs/docs/guides/intro.md
  • docs/package.json
  • docs/scripts/flatten-llms-corpus.test.mjs
  • docs/scripts/flatten-llms-gate.test.mjs
  • docs/scripts/flatten-llms-tabs.indent.test.mjs
  • docs/scripts/flatten-llms-tabs.mjs
  • docs/scripts/flatten-llms-tabs.test.mjs
  • docs/scripts/package-manager-translate.test.mjs
  • docs/src/components/PackageManagerTabs/index.js
  • docs/src/components/PackageManagerTabs/styles.module.css
  • docs/src/components/PackageManagerTabs/translate.mjs
  • docs/src/theme/MDXComponents.js
💤 Files with no reviewable changes (5)
  • docs/scripts/flatten-llms-corpus.test.mjs
  • docs/scripts/flatten-llms-tabs.test.mjs
  • docs/scripts/flatten-llms-tabs.indent.test.mjs
  • docs/scripts/flatten-llms-tabs.mjs
  • docs/scripts/flatten-llms-gate.test.mjs

Comment thread docs/package.json
"docs": "docusaurus start",
"start": "docusaurus start",
"build": "docusaurus build && node scripts/flatten-llms-tabs.mjs && node scripts/strip-generated-markers.mjs",
"build": "docusaurus build && node scripts/strip-generated-markers.mjs",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve labels for standard tabs in LLM artifacts.

Line 9 removes the only tab-flattening stage. docs/CLAUDE.md states that generated Markdown loses <TabItem> labels and identifies affected pages. The published LLM artifacts will contain tab bodies without their option names.

Keep a generic tab-label preservation step, or replace it before removing flatten-llms-tabs.mjs. PackageManagerTabs children solve this component’s command output only.

As per coding guidelines, “Keep documentation and the published LLM index accurate when documentation changes affect them.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/package.json` at line 9, Update the docs build script while preserving a
generic tab-label transformation for standard Docusaurus tabs before generating
published LLM artifacts. Do not rely solely on PackageManagerTabs; retain or
replace the functionality previously provided by flatten-llms-tabs.mjs so
affected Markdown includes each tab’s option label.

Source: Coding guidelines

@allxsmith
allxsmith merged commit 92c89e6 into main Aug 1, 2026
22 checks passed
@allxsmith
allxsmith deleted the docs/pm-tabs-component branch August 1, 2026 20:56
@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 4.0.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 5.8.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 1.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants